package com.le.tester.book.concurrentchallenge.concurrenttest12;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * createTime：2025-05-08 10:09
 * description：测试BounderBuffer的生产者-消费者程序
 */
public class PutTakeTest {

    private static final ExecutorService POOL = Executors.newCachedThreadPool();
    private final AtomicInteger putSum = new AtomicInteger(0);
    private final AtomicInteger takeSum = new AtomicInteger(0);
    private final CyclicBarrier barrier;
    private final BoundBuffer<Integer> bb;
    private final BarrierTimer timer;
    private final int nTrials, nPairs;

    PutTakeTest(int capacity, int nPairs, int nTrials) {
        this.bb = new BoundBuffer<>(capacity);
        this.nTrials = nTrials;
        this.nPairs = nPairs;
        this.timer = new BarrierTimer();
        this.barrier = new CyclicBarrier(nPairs * 2 + 1, timer);
    }

    void test() {
        timer.clear();
        try {
            for (int i = 0; i < nPairs; i++) {
                POOL.execute(new Producer());
                POOL.execute(new Consumer());
            }
            barrier.await();
            barrier.await();
            long nsPerItem = timer.getTime() / (nPairs * (long) nTrials);
            System.out.println("Throughput:"+nsPerItem+"ns/item");
            assert putSum.get() == takeSum.get();
        } catch (InterruptedException | BrokenBarrierException e) {
            throw new RuntimeException(e);
        }
    }

    class Producer implements Runnable {
        @Override
        public void run() {
            int seek = (this.hashCode() ^ (int) System.nanoTime());
            int sum = 0;
            try {
                barrier.await();
                for (int i = nTrials; i > 0; i--) {
                    bb.put(seek);
                    sum += seek;
                    //seek = xorShift(seek);
                }
                putSum.getAndAdd(sum);
                barrier.await();
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }

    class Consumer implements Runnable {

        @Override
        public void run() {
            try {
                barrier.await();
                int sum = 0;
                for (int i = nTrials; i > 0; --i) {
                    sum += bb.take();

                }
                takeSum.getAndAdd(sum);
                barrier.await();
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        new PutTakeTest(10, 10, 1000);
    }
}
